import org.apache.catalina.loader.WebappClassLoader;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;

/**
 * 自定义的classloader 可以解密文件并加载
 *
 * @author uikoo9
 */
public class UClassLoader extends WebappClassLoader {

    /**
     * 算法
     */
    private String algorithmStr = "AES/ECB/PKCS5Padding";

    /**
     * key
     */
    private String keyStr = "1234";

    /**
     * 默认构造器
     *
     * @throws Exception
     */
    public UClassLoader() {
        super();
    }

    /**
     * 默认构造器
     *
     * @param parent
     * @throws Exception
     */
    public UClassLoader(ClassLoader parent) {
        super(parent);
    }

    /*
     * (non-Javadoc)
     *
     * @see
     * org.apache.catalina.loader.WebappClassLoader#findClass(java.lang.String)
     */
    public Class findClass(String name) throws ClassNotFoundException {
        if (name.contains("com.sean")) {
            return findClassEncrypt(name);
        } else {
            return super.findClass(name);
        }
    }

    /**
     * 查找class
     *
     * @param name
     * @return
     * @throws ClassNotFoundException
     */
    private Class findClassEncrypt(String name) throws ClassNotFoundException {
        byte[] classBytes = null;
        try {
            classBytes = loadClassBytesEncrypt(name);
        } catch (Exception e) {
            e.printStackTrace();
        }

        Class cl = defineClass(name, classBytes, 0, classBytes.length);
        if (cl == null) {
            System.out.println("找不到该类:" + name);
            throw new ClassNotFoundException(name);
        }
        return cl;
    }

    /**
     * 加载加密后的class字节流
     *
     * @param name
     * @return
     * @throws Exception
     */
    private byte[] loadClassBytesEncrypt(String name) throws Exception {
        // 获取当前文件路径
        // File directory = new File("");
        // String parentPath = directory.getAbsolutePath()+File.separator;
        String parentPath = "/opt/tomcat/webapps/servletdemo/WEB-INF/classes";

        String cname = null;
        File file = null;

        cname = parentPath + name.replace('.', '/') + ".class";
        file = new File(cname);
        if (file != null) {
            if (!file.exists()) {
                throw new Exception("File Not found:" + cname);
            }
        }
        FileInputStream in = new FileInputStream(cname);
        try {
            ByteArrayOutputStream buffer = new ByteArrayOutputStream();
            byte[] buf = new byte[1024 * 100];// 100KB
            int len = 0;
            while ((len = in.read(buf)) != -1) {
                buffer.write(buf, 0, len);
            }
            in.close();
            return decrypt(parseHexStr2Byte(new String(buffer.toByteArray())));

        } finally {
            in.close();
        }
    }

    /**
     * 解密
     *
     * @param content
     * @return
     */
    private byte[] decrypt(byte[] content) {
        try {
            byte[] keyBytes = this.keyStr.getBytes("utf-8");
            SecretKeySpec key = new SecretKeySpec(keyBytes, "AES");
            Cipher cipher = Cipher.getInstance(this.algorithmStr);
            cipher.init(Cipher.DECRYPT_MODE, key);
            byte[] result = cipher.doFinal(content);
            return result;
        } catch (UnsupportedEncodingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (NoSuchPaddingException e) {
            e.printStackTrace();
        } catch (InvalidKeyException e) {
            e.printStackTrace();
        } catch (IllegalBlockSizeException e) {
            e.printStackTrace();
        } catch (BadPaddingException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 十六进制字符串转字节
     *
     * @param hexStr
     * @return
     */
    private byte[] parseHexStr2Byte(String hexStr) {
        if (hexStr.length() < 1)
            return null;
        byte[] result = new byte[hexStr.length() / 2];
        for (int i = 0; i < hexStr.length() / 2; i++) {
            int high = Integer.parseInt(hexStr.substring(i * 2, i * 2 + 1), 16);
            int low = Integer.parseInt(hexStr.substring(i * 2 + 1, i * 2 + 2), 16);
            result[i] = (byte) (high * 16 + low);
        }
        return result;
    }
}
